// *************************************
// * Creator:SunnyPaine
// * DateTime:2024/08/14 17:08:24
// * Description:<Description>
// * CopyRight:
// * ***********************************

using SunnyPaine.Autofac.Extension.Stereotype.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SunnyPaine.Autofac.Extension.Mvvm.UI
{
    /// <summary>
    /// 视图的服务容器。作用于MVVM架构中在ViewModel中获取所关联的UI中的Behavior依赖属性的组件。
    /// </summary>
    public class UIServiceContainer
    {
        private class InstanceInfo
        {
            internal Type InstanceType { get; set; }
            internal object Instance { get; set; }

            internal object[] CtorsParameters { get; set; }
        }


        private readonly Dictionary<Type, Dictionary<string, InstanceInfo>> instanceContainer;


        /// <summary>
        /// 使用默认参数创建 <see cref="UIServiceContainer"/> 的实例。
        /// </summary>
        public UIServiceContainer()
        {
            this.instanceContainer = new Dictionary<Type, Dictionary<string, InstanceInfo>>();
        }


        #region 私有方法
        /// <summary>
        /// 注册实例。
        /// </summary>
        /// <param name="from">实例的定义。接口、抽象类、类的父类、类。</param>
        /// <param name="to">实例的实现。类。</param>
        /// <param name="ctorsParameters">构造方法的参数。</param>
        /// <param name="key">指代实例实现的名称，唯一。</param>
        /// <exception cref="KeyNotFoundException"></exception>
        /// <exception cref="DuplicateException"></exception>
        private object RegisterInstance(Type from, Type to, object[] ctorsParameters, string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new KeyNotFoundException("key 不能为null。");
            }

            if (!this.instanceContainer.ContainsKey(from))
            {
                this.instanceContainer[from] = new Dictionary<string, InstanceInfo>();
            }
            if (this.instanceContainer[from].ContainsKey(key))
            {
                throw new DuplicateException($"已存在名为 {key} 的实例。");
            }

            InstanceInfo ii = new InstanceInfo()
            {
                InstanceType = to,
                Instance = ctorsParameters == null
                    ? Activator.CreateInstance(to)
                    : Activator.CreateInstance(to, ctorsParameters)
            };
            this.instanceContainer[from].Add(key, ii);

            return ii.Instance;
        }

        /// <summary>
        /// 获取实例。
        /// </summary>
        /// <param name="from">实例的定义。接口、抽象类、类的父类、类。</param>
        /// <param name="key">指代实例实现的名称，唯一。</param>
        /// <param name="isNew">是否获取一个新的实例。true：新的实例；false：单例。</param>
        /// <returns></returns>
        private object ResolveInstance(Type from, string key, bool isNew)
        {
            if (!this.instanceContainer.ContainsKey(from))
            {
                return null;
            }
            if (this.instanceContainer[from].Values.Count == 0)
            {
                return null;
            }

            InstanceInfo info = !string.IsNullOrEmpty(key) && this.instanceContainer[from].ContainsKey(key)
                ? this.instanceContainer[from][key]
                : this.instanceContainer[from].Values.FirstOrDefault();
            if (info == null)
            {
                return null;
            }
            if (!isNew)
            {
                return info.Instance;
            }
            return info.CtorsParameters != null && info.CtorsParameters.Length > 0
                ? Activator.CreateInstance(from, info.CtorsParameters)
                : Activator.CreateInstance(from);
        }
        #endregion


        /// <summary>
        /// 注册实例。
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="key"></param>
        public void Register(object instance, string key = null)
        {
            Type type = instance.GetType();
            if (string.IsNullOrEmpty(key))
            {
                key = type.FullName;
            }

            if (!this.instanceContainer.ContainsKey(type))
            {
                this.instanceContainer[type] = new Dictionary<string, InstanceInfo>();
            }
            if (this.instanceContainer[type].ContainsKey(key))
            {
                throw new DuplicateException($"已存在名为 {key} 的实例。");
            }
            this.instanceContainer[type].Add(key, new InstanceInfo()
            {
                InstanceType = type,
                Instance = instance
            });

            //取其接口作为定义注入
            Type[] interfaces = type.GetInterfaces();
            if (interfaces.Length == 0)
            {
                return;
            }
            foreach (Type item in interfaces)
            {
                if (item.FullName.StartsWith("System."))
                {
                    continue;
                }
                if (!this.instanceContainer.ContainsKey(item))
                {
                    this.instanceContainer[item] = new Dictionary<string, InstanceInfo>();
                }
                if (this.instanceContainer[item].ContainsKey(key))
                {
                    throw new DuplicateException($"已存在名为 {key} 的实例。");
                }
                this.instanceContainer[item].Add(key, new InstanceInfo()
                {
                    InstanceType = item,
                    Instance = instance
                });
            }
        }

        /// <summary>
        /// 注册实例。
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T"></typeparam>
        public void Register<T>(string key = null)
        {
            Type type = typeof(T);
            if (string.IsNullOrEmpty(key))
            {
                key = type.FullName;
            }
            _ = this.RegisterInstance(type, type, null, key);
        }

        /// <summary>
        /// 注册实例。
        /// </summary>
        /// <param name="consturctParameters"></param>
        /// <param name="key"></param>
        /// <typeparam name="T"></typeparam>
        public void Register<T>(object[] consturctParameters, string key = null)
        {
            Type type = typeof(T);
            if (string.IsNullOrEmpty(key))
            {
                key = type.FullName;
            }
            _ = this.RegisterInstance(type, type, consturctParameters, key);
        }

        /// <summary>
        /// 注册实例。
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        public void Register<TFrom, TTo>(string key = null)
        {
            Type typeFrom = typeof(TFrom);
            if (string.IsNullOrEmpty(key))
            {
                key = typeFrom.FullName;
            }
            _ = this.RegisterInstance(typeFrom, typeof(TTo), null, key);
        }

        /// <summary>
        /// 注册实例。
        /// </summary>
        /// <param name="consturctParameters"></param>
        /// <param name="key"></param>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        public void Register<TFrom, TTo>(object[] consturctParameters, string key = null)
        {
            Type typeFrom = typeof(TFrom);
            if (string.IsNullOrEmpty(key))
            {
                key = typeFrom.FullName;
            }
            _ = this.RegisterInstance(typeFrom, typeof(TTo), consturctParameters, key);
        }

        /// <summary>
        /// 获取实例。
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public object Resolve(Type t)
        {
            return this.ResolveInstance(t, null, false);
        }

        /// <summary>
        /// 获取实例。
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Resolve<T>(string key = null)
        {
            return (T)this.ResolveInstance(typeof(T), key, false);
        }

        /// <summary>
        /// 获取新的实例。
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public object ResolveNew(Type t)
        {
            return this.ResolveInstance(t, null, true);
        }

        /// <summary>
        /// 获取新的实例。
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T ResolveNew<T>(string key = null)
        {
            return (T)this.ResolveInstance(typeof(T), key, true);
        }

        /// <summary>
        /// 取消注册实例。
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T"></typeparam>
        public void UnRegister<T>(string key = null)
        {
            Type type = typeof(T);
            if (string.IsNullOrEmpty(key))
            {
                key = type.FullName;
            }
            if (!this.instanceContainer.ContainsKey(type))
            {
                return;
            }
            if (!this.instanceContainer[type].ContainsKey(key))
            {
                return;
            }
            _ = this.instanceContainer[type].Remove(key);
            if (this.instanceContainer[type].Count == 0)
            {
                _ = this.instanceContainer.Remove(type);
            }
        }

        /// <summary>
        /// 取消注册实例。
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="key"></param>
        public void UnRegister(object instance, string key = null)
        {
            Type type = instance.GetType();
            if (string.IsNullOrEmpty(key))
            {
                key = type.FullName;
            }
            if (!this.instanceContainer.ContainsKey(type))
            {
                return;
            }
            if (!this.instanceContainer[type].ContainsKey(key))
            {
                return;
            }
            _ = this.instanceContainer[type].Remove(key);
            if (this.instanceContainer[type].Count == 0)
            {
                _ = this.instanceContainer.Remove(type);
            }
        }
    }
}